/*******************************************************************************
 * This file is part of blueprint4j www.blueprint4j.org.
 * 
 *     blueprint4j is free software: you can redistribute it and/or modify
 *     it under the terms of the GNU Lesser General Public License as published by
 *     the Free Software Foundation, either version 3 of the License, or
 *     (at your option) any later version.
 * 
 *     blueprint4j is distributed in the hope that it will be useful,
 *     but WITHOUT ANY WARRANTY; without even the implied warranty of
 *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *     GNU Lesser General Public License for more details.
 * 
 *     You should have received a copy of the GNU Lesser General Public License
 *     along with blueprint4j. If not, see <http://www.gnu.org/licenses/>.
 ******************************************************************************/
package blueprint4j.db;

import java.sql.SQLException;
import java.util.Vector;

import blueprint4j.utils.Log;
import blueprint4j.utils.VectorInteger;
import blueprint4j.utils.VectorString;

/**
 * This class creates stores index information on specific classes
 */

public class Index implements java.io.Serializable {
	private static final long serialVersionUID = 1L;

	public VectorString fields = new VectorString();
	public String table_name = null,index_name = null;
	private VectorInteger pos = new VectorInteger();

	protected static String getPrimaryIndexName(String table_name) {
		return table_name+"_uidx_id";
		//return table_name+"_ui";
	}

	protected Index(String table_name,String index_name,VectorString fields,int pos)
	throws DataException {
		this.fields = fields;
		this.table_name = table_name;
		this.index_name = index_name;
		this.pos.add(new Integer(pos));
	}

	protected void addToFields(String field,int pos) {
		if (!fields.contains(field)) {
			fields.add(field);
			this.pos.add(new Integer(pos));
		}
	}
	
	public Index(String index_name,Field fields[],Entity entity) {
		try {
			entity.addIndex(this);
			table_name = entity.getTableName();
			this.index_name = table_name+"_"+index_name;
			for (int i = 0;i < fields.length;i++) {
				this.fields.add(fields[i].getName());
			}
		} catch (Exception exception) {
			exception.printStackTrace();
			Log.critical.out(exception);
			System.exit(-1);
		}
	}

	protected void resort() {
		VectorString f = new VectorString();
		for (int i =0;i < fields.size();i++) {
			f.add(fields.get(pos.get(i).intValue()-1));
			pos.set(i,new Integer(i));
		}
		this.fields = f;
	}
	
	public boolean equals(Object object) {
		if (object instanceof Index) {
			return ((Index) object).index_name.equalsIgnoreCase(index_name) &&
			       ((Index) object).fields.equalsIgnoreCase(fields);
		}
		return false;
	}

	public boolean equals(VectorString fields) {
		return this.fields.equals(fields);
	}

	/**
	 * Makes sure that index exists, and is correct
	 */
	public void updateIndex(VectorIndex indexes,DBConnection connection)
	throws SQLException,DataException {
		for (int i =0;i < indexes.size();i++) {
			if (indexes.get(i).index_name.equalsIgnoreCase(index_name)) {
				if (indexes.get(i).equals(this)) {
					return;
				} else {
					// DROP AND RECREATE
					connection.getDataBaseUtils().dropIndex(connection,table_name,indexes.get(i).index_name);
					connection.commit();
					indexes.remove(i--);
					updateIndex(indexes,connection);
					return;
				}
			}
		}
		Log.trace.out("UPDATING DATABASE INDEX","CREATING INDEX FOR ["+table_name+"] ["+fields.toString()+"]");
		connection.execute(connection.getDataBaseUtils().getCreateIndexSQL(this));
		indexes.add(this);
	}

	public static class UniqueIndex extends Index {
		public UniqueIndex(String index_name,Field fields[],Entity entity) {
			super(index_name,fields,entity);
		}
	}

	public static class VectorIndex {
		private Vector store = new Vector();

		public VectorIndex(){}

		/**
		 * This loads from the database meta data
		 */
		public static VectorIndex getIndexForTable(DBConnection connection,String tabname)
		throws SQLException {
			Index.VectorIndex indexes = new Index.VectorIndex();
			for (
				java.sql.ResultSet rs = connection.getMetaData().getIndexInfo((String)null,(String)null,connection.getDataBaseUtils().convertTabnameToLocal(tabname),false,false);
				rs.next();
			) { 
				String index_name = rs.getString(6);
				if (index_name.equals("PRIMARY")) {
					index_name = Index.getPrimaryIndexName(tabname);
				}
				if (!indexes.containsIndexName(index_name)) {
					Index index = new Index(tabname,index_name,new VectorString(new String[]{rs.getString(9)}),rs.getInt(8));
					indexes.add(index);
				} else {
					indexes.getFromIndexName(index_name).addToFields(rs.getString(9),rs.getInt(8));
				}
			}
			indexes.resort();
			return indexes;
		}

		public void updateIndex(VectorIndex indexes,DBConnection connection)
		throws SQLException,DataException {
			for (int i = 0;i < size();i++) {
				get(i).updateIndex(indexes,connection);
			}
		}

		public Index get(int pos) {
			return (Index)store.get(pos);
		}
		
		/**
		 * Returns a Index filtered by index name
		 */
		public Index getFromIndexName(String indxname) {
			for (int i= 0;i < size();i++) {
				if (get(i).index_name.equalsIgnoreCase(indxname)) {
					return get(i);
				}
			}
			return null;
		}

		public VectorIndex getIndexesFromTablename(String tabname) {
			VectorIndex vi = new VectorIndex();
			for (int i =0;i < size();i++) {
				if (get(i).table_name.equals(tabname)) {
					vi.add(get(i));
				}
			}
			return vi;
		}
		
		public void resort() {
			for (int i = 0;i < size();i++) {
				get(i).resort();
			}
		}
		
		public VectorIndex get(String table_name) {
			VectorIndex vi = new VectorIndex();
			for (int i = 0;i < size();i++) {
				if (get(i).table_name.equals(table_name)) {
					vi.add(get(i));
				}
			}
			return vi;
		}

		/**
		 * Drops all indexes that are in the database but bot defined in the entity
		 */
		public void dropExtraIndexes(DBConnection connection,VectorIndex all_index,String table_name)
		throws SQLException,DataException {
			VectorIndex local = all_index.get(table_name);
			for (int i =0;i < local.size();i++) {
				if (!contains(local.get(i))) {
					connection.getDataBaseUtils().dropIndex(connection,local.get(i).table_name,local.get(i).index_name);
					connection.commit();
				}
			}
		}

		public boolean contains(Index item){
			for (int i= 0;i< size();i++) {
				if (get(i).equals(item)) {
					return true;
				}
			}
			return false;
		}
		
		public boolean containsIndexName(String indxname) {
			for (int i =0;i < size();i++) {
				if (get(i).index_name.equalsIgnoreCase(indxname)) {
					return true;
				}
			}
			return false;
		}

		public void add(Index item) {
			store.add(item);
		}

		public void add(VectorIndex item) {
			for (int i =0;i < item.size();i++) {
				add(item.get(i));
			}
		}

		public boolean remove(Index item) {
			return store.remove(item);
		}

		public Index remove(int pos) {
			return (Index) store.remove(pos);
		}

		public int size() {
			return store.size();
		}

		public void clear(){
			store.clear();
		}

		public boolean equals(VectorString vs) {
			for (int i =0;i < size();i++) {
				if (get(i).equals(vs)) {
					return true;
				}
			}
			return false;
		}
	}
}
